package com.gun3y.entity.visual;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.gun3y.entity.Wrapper;
import com.gun3y.entity.media.Data;
import com.gun3y.entity.media.Movie;
import com.gun3y.entity.media.PropertyType;
import com.gun3y.entity.media.WrapperDataProps;
import com.gun3y.entity.media.WrapperObjectProps;
import com.gun3y.entity.pattern.AttributePattern;
import com.gun3y.entity.pattern.FieldPattern;
import com.gun3y.entity.pattern.FieldPatternExpression;
import com.gun3y.entity.pattern.PositionPattern;
import com.gun3y.entity.pattern.TagPattern;

public class TreeNode {

    private String name;

    private List<TreeNode> children;

    private static List<TreeNode> createTreeNode(List<Data> datas) {
	List<TreeNode> nodes = new ArrayList<TreeNode>();
	if (datas != null) {
	    Map<String, List<TreeNode>> map = new HashMap<String, List<TreeNode>>();
	    for (Data data : datas) {
		if (!map.containsKey(data.getText()))
		    map.put(data.getText(), new ArrayList<TreeNode>());
		map.get(data.getText()).add(new TreeNode(data.getFrom()));
	    }

	    for (Entry<String, List<TreeNode>> entry : map.entrySet()) {
		TreeNode n = new TreeNode(entry.getKey(), entry.getValue());
		nodes.add(n);
	    }
	}
	return nodes;
    }

    public static TreeNode createMovieTree(List<Movie> movies) {
	TreeNode root = new TreeNode("root");
	root.children = new ArrayList<TreeNode>();
	if (movies != null) {
	    for (Movie movie : movies) {
		TreeNode node = new TreeNode(movie.getName());
		node.children = new ArrayList<TreeNode>();
		for (PropertyType p : PropertyType.values()) {
		    if (p != PropertyType.name) {
			node.children.add(new TreeNode(p.toString(), createTreeNode(p.getData(movie))));
		    }
		}
		root.children.add(node);
	    }
	}
	return root;
    }

    public static TreeNode createWrapperTree(List<Wrapper> wrappers) {
	TreeNode root = new TreeNode("root");
	root.children = new ArrayList<TreeNode>();
	if (wrappers != null) {
	    for (Wrapper wrapper : wrappers) {
		TreeNode node = new TreeNode(wrapper.getName());
		node.children = new ArrayList<TreeNode>();
		node.children.add(new TreeNode(WrapperDataProps.name + "", Arrays.asList(new TreeNode(wrapper.getName()))));
		node.children.add(new TreeNode(WrapperDataProps.base + "", Arrays.asList(new TreeNode(wrapper.getBase()))));
		node.children.add(new TreeNode(WrapperDataProps.description + "", Arrays.asList(new TreeNode(wrapper.getDescription()))));
		node.children.add(new TreeNode(WrapperDataProps.encoding + "", Arrays.asList(new TreeNode(wrapper.getEncoding()))));

		if (wrapper.getFields() != null) {
		    List<TreeNode> fields = new ArrayList<TreeNode>();
		    for (FieldPattern fieldPattern : wrapper.getFields()) {
			TreeNode fieldNode = new TreeNode(fieldPattern.getFieldName().name());
			fieldNode.children = new ArrayList<TreeNode>();
			fieldNode.children.add(new TreeNode(WrapperDataProps.fieldName + "", Arrays.asList(new TreeNode(fieldPattern.getFieldName().name()))));
			fieldNode.children.add(new TreeNode(WrapperDataProps.fieldAttribute + "", Arrays.asList(new TreeNode(fieldPattern.getTagAttribute()))));
			fieldNode.children.add(new TreeNode(WrapperDataProps.fieldType + "", Arrays.asList(new TreeNode(fieldPattern.getFieldType().name()))));
			fieldNode.children.add(new TreeNode(WrapperDataProps.multipleResults + "", Arrays.asList(new TreeNode(""
				+ fieldPattern.isMultipleResults()))));

			if (fieldPattern.getExpressions() != null) {
			    List<TreeNode> fieldExpressions = new ArrayList<TreeNode>();
			    for (FieldPatternExpression fieldExp : fieldPattern.getExpressions()) {
				TreeNode fieldExpNode = new TreeNode(fieldExp.getPattern());
				fieldExpNode.children = new ArrayList<TreeNode>();
				fieldExpNode.children.add(new TreeNode(WrapperDataProps.pattern + "", Arrays.asList(new TreeNode(fieldExp.getPattern()))));
				fieldExpNode.children.add(new TreeNode(WrapperDataProps.group + "", Arrays.asList(new TreeNode(fieldExp.getGroup()))));

				fieldExpNode.children.add(new TreeNode(WrapperDataProps.inclusive + "",
					Arrays.asList(new TreeNode(fieldExp.isInclusive() + ""))));

				fieldExpNode.children.add(new TreeNode(WrapperDataProps.expressionType + "", Arrays.asList(new TreeNode(fieldExp
					.getExpressionType().name()))));
				fieldExpressions.add(fieldExpNode);
			    }
			    fieldNode.children.add(new TreeNode(WrapperObjectProps.hasPatternExpression + "", fieldExpressions));
			}

			if (fieldPattern.getPosition() != null) {
			    List<TreeNode> posititions = new ArrayList<TreeNode>();
			    PositionPattern position = fieldPattern.getPosition();
			    if (position.getChildPattern() != null) {
				posititions.add(createTagTreeNode(position.getChildPattern(), WrapperObjectProps.hasChildTag));
			    }
			    if (position.getLocalPattern() != null) {
				posititions.add(createTagTreeNode(position.getLocalPattern(), WrapperObjectProps.hasLocalTag));
			    }
			    if (position.getNextSiblingPattern() != null) {
				posititions.add(createTagTreeNode(position.getNextSiblingPattern(), WrapperObjectProps.hasNextsiblingTag));
			    }
			    if (position.getParentPattern() != null) {
				posititions.add(createTagTreeNode(position.getParentPattern(), WrapperObjectProps.hasParentTag));
			    }
			    if (position.getPreSiblingPattern() != null) {
				posititions.add(createTagTreeNode(position.getPreSiblingPattern(), WrapperObjectProps.hasPresiblingTag));
			    }

			    fieldNode.children.add(new TreeNode(WrapperObjectProps.hasPosition + "", posititions));
			}

			fields.add(fieldNode);
		    }
		    node.children.add(new TreeNode(WrapperObjectProps.hasFieldPattern + "", fields));
		}

		root.children.add(node);
	    }
	}
	return root;
    }

    private static TreeNode createTagTreeNode(TagPattern tagPattern, WrapperObjectProps label) {
	TreeNode tagPatternNode = new TreeNode(label + "");
	tagPatternNode.children = new ArrayList<TreeNode>();
	tagPatternNode.children.add(new TreeNode(WrapperDataProps.tagName + "", Arrays.asList(new TreeNode(tagPattern.getTagName()))));
	tagPatternNode.children.add(new TreeNode(WrapperDataProps.tagValue + "", Arrays.asList(new TreeNode(tagPattern.getTagValue()))));
	tagPatternNode.children.add(new TreeNode(WrapperDataProps.tagExpression + "", Arrays.asList(new TreeNode(tagPattern.getTagExpression().name()))));
	tagPatternNode.children.add(new TreeNode(WrapperDataProps.recursive + "", Arrays.asList(new TreeNode(tagPattern.isRecursive() + ""))));

	if (tagPattern.getAttributes() != null) {
	    List<TreeNode> attributes = new ArrayList<TreeNode>();
	    for (AttributePattern attPattern : tagPattern.getAttributes()) {
		TreeNode attPatternNode = new TreeNode(attPattern.getAttributeName());
		attPatternNode.children = new ArrayList<TreeNode>();
		attPatternNode.children.add(new TreeNode(WrapperDataProps.attributeName + "", Arrays.asList(new TreeNode(attPattern.getAttributeName()))));
		attPatternNode.children.add(new TreeNode(WrapperDataProps.attributeValue + "", Arrays.asList(new TreeNode(attPattern.getAttributeValue()))));
		attPatternNode.children.add(new TreeNode(WrapperDataProps.attributeExpression + "", Arrays.asList(new TreeNode(attPattern
			.getAttributeExpression().name()))));

		attributes.add(attPatternNode);
	    }

	    tagPatternNode.children.add(new TreeNode(WrapperObjectProps.hasAttribute + "", attributes));
	}

	return tagPatternNode;
    }

    public TreeNode(String name, List<TreeNode> children) {
	super();
	this.name = name;
	this.children = children;
    }

    public TreeNode(String name) {
	super();
	this.name = name;
    }

    public String getName() {
	return name;
    }

    public void setName(String name) {
	this.name = name;
    }

    public List<TreeNode> getChildren() {
	return children;
    }

    public void setChildren(List<TreeNode> children) {
	this.children = children;
    }

    @Override
    public String toString() {
	Gson gson = (new GsonBuilder()).setPrettyPrinting().create();
	return gson.toJson(this, this.getClass());
    }

}
